//
//  main.m
//  OC07_内存管理
//
//  Created by Scott on 16/3/3.
//  Copyright © 2016年 Scott. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Person.h"

#pragma mark - xcode如何查看内存内容.
void viewMemory() {
    
    int a = 100;
    int b = 200;
    
    int * p = NULL;
    p = &b;
    
    NSLog(@"over");
    
}


#pragma mark - 知识点1: 对象及对象的实例变量在堆中的地址
void chapter1() {
    
    /** 
     * 定义Person类: 实例变量可见度为 @public
     * 详见 Person类 文件
     */
    
#if 0
    /** 创建对象Person1, 并赋值 */
    Person *person1 = [[Person alloc] init];
    person1->_name = @"zhangsan";
    person1->_sex = @"male";
    person1->_age = 18;
    
    /* 直接显示 对象大小 . 需要导入<objc/runtime.h> 文件 */
    NSLog(@"size:%ld", class_getInstanceSize([person1 class]));
    NSLog(@"************************************************");
    NSLog(@"---%d---(person1)对象和对象的实例变量在堆中的地址---", __LINE__);
    
    NSLog(@"person1      add:%p", person1);
    NSLog(@"person1 name add:%p", &person1->_name);
    NSLog(@"person1 age  add:%p", &person1->_age);
    NSLog(@"person1 sex  add:%p", &person1->_sex);
    
    NSLog(@"***********************************************");
    NSLog(@"---如果实例变量类型是对象类型, 指针内容是一个指针---");
    NSLog(@"---%d---(person1)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person1 name contant :%p", person1->_name);
    NSLog(@"person1 sex  contant :%p", person1->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    /*!^ 结论
     * 实例变量的指针最终指向了字符串常量首地址
     */
    
    NSLog(@"---如果实例变量类型是非对象类型, 指针内容即为值---");
    NSLog(@"person1 age  contant :%p", person1->_age);
    NSLog(@"person1 age  contant :%ld", person1->_age);
    
    
    /** 创建另一个对象Person2 */
    Person *person2 = [[Person alloc] init];
    person2 = person1;
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person2      add:%p", person2);
    NSLog(@"person2 name add:%p", &person2->_name);
    NSLog(@"person2 age  add:%p", &person2->_age);
    NSLog(@"person2 sex  add:%p", &person2->_sex);
    /*!^ 结论
     * person2 指向了person1的首地址
     * person2 的实例变量地址也指向person1 的实例变量首地址
     */
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person2)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person2 name contant :%p", person2->_name);
    NSLog(@"person2 sex  contant :%p", person2->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    
    
    /* 使用copy方法创建person3 对象 */
    Person *person3 = [person1 copy];
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person3)对象和对象的实例变量在堆中的地址---", __LINE__);
    NSLog(@"person3      add:%p", person3);
    NSLog(@"person3 name add:%p", &person3->_name);
    NSLog(@"person3 age  add:%p", &person3->_age);
    NSLog(@"person3 sex  add:%p", &person3->_sex);
    
    
    NSLog(@"************************************************");
    NSLog(@"---%d---(person3)实例变量的指针指向的地址---", __LINE__);
    NSLog(@"person3 name contant :%p", person3->_name);
    NSLog(@"person3 sex  contant :%p", person3->_sex);
    NSLog(@"字符串(zhangsan)   add:%p", @"zhangsan");
    NSLog(@"字符串(male)       add:%p", @"male");
    
#endif
    
}

#pragma mark - 知识点2: 内存管理
void chapter2() {
    
    /** 
     * 内存管理方式: 垃圾回收机制, MRC(人工引用计数), ARC(自动引用计数).
     *
     * OS X开发, 用垃圾回收机制, iOS开发: MRC,ARC
     */
    
}

#pragma mark - 知识点3: 影响引用计数的方法
void chapter3() {

#if 0
    /** alloc 方法. +1 */
    Person *per1 = [[Person alloc] init];
    
    NSUInteger count = [per1 retainCount];
    NSLog(@"count:%ld", count);
    
    
    /** retain 方法. +1 */
    [per1 retain]; /**< count:2 */
    NSLog(@"count:%ld", [per1 retainCount]);
    
    [per1 retain]; /**< count:3 */
    NSLog(@"count:%ld", [per1 retainCount]);
    
    
    /** release 方法. -1 */
    [per1 release]; /**< count:2 */
    NSLog(@"count:%ld", [per1 retainCount]);
    
    [per1 release]; /**< count:1 */
    
    [per1 release]; /**< 此时引用计数count为0 */
    NSLog(@"count:%ld", [per1 retainCount]);
    
//    [per1 release];
//    NSLog(@"count:%ld", [per1 retainCount]);
#endif
    
#if 0  /** autorelease 方法. (-1)*/
    
    Person *per1 = [[Person alloc] init];
    NSLog(@"count:%ld", [per1 retainCount]); /** 1 */
    
    [per1 retain]; /**< 2 */
    
    [per1 autorelease]; /**< 此时, 引用计数并没有减1, 是在未来某个时刻减1. */
    NSLog(@"count:%ld", [per1 retainCount]);
    
#endif
    
#if 0 /** autoreleasespool 自动释放池. */
    
    Person *person1 = nil;
    Person *person2 = [[Person alloc] init];
    
    @autoreleasepool {
        // 自动释放池内部.
        
        person1 = [[Person alloc] init];
        [person1 retain]; /**< 2 */
        [person1 retain]; /**< 3 */
        [person1 autorelease];
        NSLog(@"count:%ld", [person1 retainCount]);
        
        [person2 retain];
        
    }

    
    NSLog(@"count:%ld", [person1 retainCount]);
    NSLog(@"person2 count:%ld", [person2 retainCount]);
    
#endif
    
#if 1  /** dealloc 方法. */
    
    Person *per1 = [[Person alloc] init];
    
    per1.name = @"zhangsan";
    NSLog(@"name:%@", per1.name);
    
    [per1 release]; /** 此时, 引用计数为0, 对象自动调用dealloc方法, 释放内存. 为了观察dealloc过程, 重写dealloc方法. */
#endif
}

#pragma mark - 知识点4: copy方法
void chapter4() {
    
    Person *person = [[Person alloc] init];
    
    person.name = @"zhangsan";
    person.age = 20;
    person.sex = @"male";
    
    // 创建对象副本.
    Person *person2 = [person copy];
    
}

#pragma mark - 知识点5: 属性的内部实现原理
void chapter5() {
    
    /** 详见, Person类 的setter方法 和 getter方法.  */
    
    
}

#pragma mark - 知识点6: dealloc内释放实例变量
void chapter6() {
    
    /** 详见, Person类 dealloc方法. */
    
}


#pragma mark - 知识点7: 便利构造器方法的实现原理
void chapter7() {
    
    /** 详见 Person的构造器方法. 
     * 注意: 使用构造器方法创建对象, 后面不能release. 否则过度释放.
     */
    
    Person *person = [Person personWithName:@"Tom" sex:@"male" age:20];
    
    // coding...
    
}


#pragma mark - 知识点8: collection 的内存管理
void chapter8() {
    
    /** 
     * 容器 自动管理 里面元素对象的引用计数.
     *
     * 对象加入到容器中, 引用计数会自动 +1
     * 对象从容器中移除, 引用计数会自动 -1
     *
     * 容器对象销毁, 在销毁前会对里面所有对象发送release消息(即引用计数 -1).
     */
    
}


#pragma mark - MRC下一个自定义类的常用写法.
void chapter9() {
    
    /** 详见. Film类. */
}


#pragma mark - main函数.
int main(int argc, const char * argv[]) {
    
//    viewMemory();
//    chapter1();
//    chapter2();
//    chapter3();
//    chapter4();
//    chapter5();
//    chapter6();
//    chapter7();
//    chapter8();
    chapter9();
    
    return 0;
}
